package cz.possoft.explorer.context;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import cz.possoft.explorer.ServiceFactory;
import cz.possoft.explorer.meta.ClassMetadata;
import cz.possoft.explorer.meta.DomainMetadata;
import cz.possoft.explorer.resolver.Resolver;

/**
 * ObjectContextBuilder is able to create instance of ObjectContext from given Object (main functionality is in method <b>build</b>)
 * 
 * @author <a href="mailto:mposolda@gmail.com">Marek Posolda</a>
 * @version $Revision$
 */
public class ObjectContextBuilder
{
   /**
    * Creating ObjectContext from given Object.
    * 
    * @param <V>
    * @param <T>
    * @param object
    * @return ObjectContext instance for given Object
    */
   public <V,T> List<ObjectContext<? super V,T>> build(Object object)
   {
      DomainMetadata domainMetadata = ServiceFactory.getInstance().getDomainMetadata();      
      
      List<ObjectContext<? super V,T>> resultObjectContexts = new LinkedList<ObjectContext<? super V,T>>();
      
      // TODO: Verify if type-safety is ok
      ClassMetadata<? super V,T> classMetadata = domainMetadata.getClassMetadata((Class<V>)object.getClass());
      while (classMetadata != null)
      {
         // Now check the main class and 
         Class<? super V> clazz = classMetadata.getClazz();
         List<FieldContext<?,T>> fields = getFieldContexts(classMetadata, (V)object, domainMetadata);
         
         // TODO: it's not clear why it's not working (constructor new ObjectContext<? super V>)
         ObjectContext<? super V,T> objectContext = new ObjectContext(object, fields, clazz);
         resultObjectContexts.add(objectContext);
         
         // Add interface types first into objectcontexts list
         // TODO: is this really correct? Can be interfaces treated as ? super V. Check...
         Class<? super V>[] interfaceClasses = (Class<? super V>[])clazz.getInterfaces();
         for (Class<? super V> interfaceClass : interfaceClasses)
         {
            ClassMetadata<? super V,T> interfaceMetadata = domainMetadata.getClassMetadata(interfaceClass);
            fields = getFieldContexts(interfaceMetadata, (V)object, domainMetadata);            
            objectContext = new ObjectContext(object, fields, interfaceClass);            
            resultObjectContexts.add(objectContext);
         }         
           
         // And now assign metadata of supertype
         classMetadata = domainMetadata.getClassMetadata(clazz.getSuperclass());
      }

      return resultObjectContexts;
   }
   
   private <V,C> List<FieldContext<?,C>> getFieldContexts(ClassMetadata<V,C> classMetadata, V object, DomainMetadata domainMetadata)
   {
      Resolver<V,C> resolver = classMetadata.getResolver();
      
      // Create list with field contexts. Resolve values of all fields.
      Map<C, Class<?>> fields = classMetadata.getFields(object);
      List<FieldContext<?,C>> resultFields = new LinkedList<FieldContext<?,C>>();
      for (Map.Entry<C, Class<?>> entry : fields.entrySet())
      {
         Object fieldValue = resolver.getFieldValue(object, entry.getKey());
         FieldModifierEnum modifier = resolver.getFieldModifier(object, entry.getKey());
         FieldContext<?,C> fieldContext = new FieldContext<Object,C>(entry.getKey(), fieldValue, ((fieldValue != null) && (domainMetadata.getClassMetadata(fieldValue.getClass()) != null)), modifier);
         resultFields.add(fieldContext);         
      }  
      return resultFields;
   }

}

